﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace IOCS.IOCR
{
    /// <summary>
    /// IOC工厂类
    /// 1：.net8 IOC创建对象
    /// 2：存储对象
    ///     含义：存储在集合当中： List Set 字典
    ///     方案：字典，因为字典随机存储、取对象的效率最高
    /// 3：取对象
    ///     含义：字典集合是私有的，则想要获取到对象则创建一个公有的方法获取
    /// 4：依赖注入
    ///     含义：对象的属性赋值（相当于new）
    ///     我们取到的对象必须是可用的。
    ///     顺序应在创建对象之后，存储对象之前赋值。
    /// 5：对象过滤
    ///     含义：只创建需要的类（比如 Program.cs、IOCFactory.cs不需要，所以需要过滤）
    ///     技术：特性IOCService。
    /// </summary>
    public class IOCFactory
    {
        /// <summary>
        /// 创建字典
        /// </summary>
        private Dictionary<string,Object> IOCR = new Dictionary<string,Object>();

        public IOCFactory() {
            //  创建Student对象
            //Student student = new Student();
            //  创建Teacher对象
            //Teacher teacher = new Teacher();

            // 问题：违背开闭原则。扩展开发，修改关闭。
            // 后果：代码不稳定
            // 原因：new
            // 方案：Activitor 

            // 将项目转换成程序集，目的是为了拿到项目中所有定义的类型，包括Student、Teacher
            Assembly assembly = Assembly.Load("IOCS");
            Type[] types = assembly.GetTypes(); // 获得所有定义类型的反射类型
            foreach (var type in types)
            {
                //5、获取自定义特性
                IOCService iOCService = type.GetCustomAttribute<IOCService>();
                //5.1、过滤
                if (iOCService != null)
                {
                    // 1、创建对象
                    Object _object = Activator.CreateInstance(type);

                    // 4、对象属性赋值（依赖注入）
                    // 技术：PropertyIn -- 属性的反射类型
                    // Student --> Teacher 如果使用 Student类，则需要实例化Student类中使用到的Teacher、**、**...,不然则会报错(未将对象实例化)
                    /* 具体依赖注入
                    PropertyInfo propertyInfo = type.GetProperty("teacher");
                    propertyInfo.SetValue(_object, new Teacher());

                    PropertyInfo propertyInfo1 = type.GetProperty("school");
                    propertyInfo.SetValue(_object, new School());*/

                    // 问题：违背开闭原则。扩展开放，修改关闭。
                    // 扩展开放，修改关闭。---> 含义：允许School中的属性发生更改，但是不能对IOCFactory发生变更
                    // 后果：依赖注入代码不稳定，因为他随时会发生更改
                    // 原因：一个个的赋值
                    // 方法：批量复制
                    // 技术：PropertyInfo[]
                    PropertyInfo[] propertyInfos = type.GetProperties();
                    foreach (var property in propertyInfos)
                    {
                        // 便利Type[]
                        foreach (Type type1 in types)
                        {
                            // 4.1 对象属性赋值
                            if (property.PropertyType.Equals(type1))
                            {
                                // 根据type1创建对象
                                Object _objectValue = Activator.CreateInstance(type1);

                                property.SetValue(_object, _objectValue);
                            }
                        }

                        // 4.1 对象属性赋值
                        /* 具体对象属性赋值
                        if (property.PropertyType.Equals(typeof(Teacher)))
                        {
                            property.SetValue(_object,new Teacher());
                        }
                        if (property.PropertyType.Equals(typeof(School)))
                        {
                            property.SetValue(_object, new School());
                        }
                        if (property.PropertyType.Equals(typeof(Doctor)))
                        {
                            property.SetValue(_object, new Doctor());
                        }*/
                    }
                    // 4.2：又违背了开闭原则
                    // 原因：使用了具体对象赋值
                    // 方案：使用抽象对象赋值  找出一个可以代替 Teacher、School、Doctor的对象  ---->  Type


                    // 2、存储对象 对象key: 命名空间 + 对象名
                    IOCR.Add(type.FullName, _object);
                }

                
            }
        }

        /// <summary>
        /// 3、取对象
        /// </summary>
        /// <param name="FullName"></param>
        /// <returns></returns>
        public Object GetObject(string FullName)
        {
            return IOCR[FullName];
        }
    }
}
